﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CNative.Utilities
{
    #region ObjectPool
    public class ObjectPool<T>
    {
        #region 
        private int isTaked = 0;
        private Queue<T> queue = new Queue<T>();
        private Func<T> func = null;
        private int currentResource = 0;
        private int tryNewObject = 0;
        private readonly int minSize = 1;
        private readonly int maxSize = 50;
        #endregion

        #region private methods
        private void Enter()
        {
            while (Interlocked.Exchange(ref isTaked, 1) != 0)
            {
            }
        }
        private void Leave()
        {
            Interlocked.Exchange(ref isTaked, 0);
        }
        #endregion

        /// <summary>
        /// 构造一个对象池
        /// </summary>
        /// <param name="func">用来初始化对象的函数</param>
        /// <param name="minSize">对象池下限</param>
        /// <param name="maxSize">对象池上限</param>
        public ObjectPool(Func<T> func, int minSize = 100, int maxSize = 100)
        {
            Utils.CheckCondition(() => func == null, "func");
            Utils.CheckCondition(() => minSize < 0, "minSize");
            Utils.CheckCondition(() => maxSize < 0, "maxSize");
            if (minSize > 0)
                this.minSize = minSize;
            if (maxSize > 0)
                this.maxSize = maxSize;
            for (var i = 0; i < this.minSize; i++)
            {
                this.queue.Enqueue(func());
            }
            this.currentResource = this.minSize;
            this.tryNewObject = this.minSize;
            this.func = func;
        }

        /// <summary>
        /// 从对象池中取一个对象出来, 执行完成以后会自动将对象放回池中
        /// </summary>
        public T GetObject()
        {
            var t = default(T);
            try
            {
                if (this.tryNewObject < this.maxSize)
                {
                    Interlocked.Increment(ref this.tryNewObject);
                    t = func();
                    // Interlocked.Increment(ref this.currentResource);
                }
                else
                {
                    this.Enter();
                    t = this.queue.Dequeue();
                    this.Leave();
                    Interlocked.Decrement(ref this.currentResource);
                }
                return t;
            }
            finally
            {
                this.Enter();
                this.queue.Enqueue(t);
                this.Leave();
                Interlocked.Increment(ref currentResource);
            }
        }

    }
    #endregion
    #region XObjectPool
    public class XObjectPool<T> : IDisposable
    {
        private bool disposed;
        private XPoolConfiguration xcfg;
        /// <summary>
        /// 初始化对象池
        /// </summary>
        /// <param name="createObject">创建XObject对象</param>
        /// <param name="activeXObject"> 获取XObject对象之前验证True 有效</param>
        public XObjectPool(Func<T> createObject, Func<T, bool> activeXObject = null) : this(
            maxActive: 1000,
            maxIdle: 400,
            minIdle: 10, createObject: createObject, activeXObject: activeXObject)
        {

        }
        /// <summary>
        /// 初始化对象池
        /// </summary>
        /// <param name="maxActive">最大活动数量</param>
        /// <param name="minIdle">最小空闲数量</param>
        /// <param name="maxIdle">最大空闲数量</param>
        /// <param name="createObject">创建XObject对象</param>
        /// <param name="activeXObject"> 获取XObject对象之前验证True 有效</param>
        public XObjectPool(int maxActive, int minIdle, int maxIdle, Func<T> createObject, Func<T, bool> activeXObject = null)
        {
            xcfg = new XPoolConfiguration()
            {
                MaxActive = maxActive,
                MaxIdle = maxIdle,
                MinIdle = minIdle
            };
            pools = new ConcurrentStack<XObject<T>>();
            ResetEvent = new AutoResetEvent(false);
            if (createObject != null)
            {
                CreateXObject = createObject;
            }
            else
            {
                throw new ArgumentNullException("createObject 不能为空");
            }
            if (activeXObject != null)
            {
                ActiveXObject = activeXObject;
            }
            Parallel.For(0, minIdle, x =>
            {
                pools.Push(new XObject<T>()
                {
                    Value = CreateXObject.Invoke(),
                    LastDateTime = DateTime.Now,
                    Pool = this
                });
            });
            StartTaskClearLongIdleXObject();
        }
        /// <summary>
        /// 初始化对象池
        /// </summary>
        /// <param name="xcfg">对象池配置</param>
        /// <param name="createObject">创建XObject对象</param>
        /// <param name="activeXObject"> 获取XObject对象之前验证True 有效</param>
        public XObjectPool(XPoolConfiguration xcfg, Func<T> createObject, Func<T, bool> activeXObject = null) : this(xcfg.MaxActive, xcfg.MinIdle, xcfg.MaxIdle, createObject, activeXObject)
        {

        }
        private ConcurrentStack<XObject<T>> pools;
        private int _activedTransportCount = 0;
        private AutoResetEvent ResetEvent { get; set; }
        /// <summary>
        /// 活动链接数量
        /// </summary>
        public int ActivedTransportCount => _activedTransportCount;

        /// <summary>
        /// 原子性增加 活动链接数量
        /// </summary>
        private void InterlockedIncrement()
        {
            Interlocked.Increment(ref _activedTransportCount);
        }
        /// <summary>
        /// 原子性减少 活动链接数量
        /// </summary>
        private void InterlockedDecrement()
        {
            Interlocked.Decrement(ref _activedTransportCount);
        }

        public XObject<T> Borrow(TimeSpan? timeout = null)
        {
            if (!pools.TryPop(out XObject<T> xobj))
            {
                if (pools.Count < xcfg.MinIdle && _activedTransportCount < xcfg.MaxActive)
                {
                    pools.Push(new XObject<T>()
                    {
                        Value = CreateXObject.Invoke(),
                        LastDateTime = DateTime.Now,
                        Pool = this
                    });
                }
                if (!pools.Any() && _activedTransportCount >= xcfg.MaxActive)
                {
                    int millisecondsTimeout = 20000;
                    if (timeout.HasValue && timeout.Value.TotalMilliseconds > 0)
                    {
                        millisecondsTimeout = (int)timeout.Value.TotalMilliseconds;
                    }
                    bool result = ResetEvent.WaitOne(millisecondsTimeout);
                    if (!result)
                    {
                        throw new TimeoutException($"Timeout对象池等待超时！");
                    }
                }
                if (!pools.TryPop(out xobj))
                {
                    xobj = new XObject<T>()
                    {
                        Value = CreateXObject.Invoke(),
                        LastDateTime = DateTime.Now,
                        Pool = this
                    };
                }
            }
            InterlockedIncrement();
            //借出之前判断对象是否有效
            if (!(ActiveXObject?.Invoke(xobj.Value) == true))
            {
                throw new InvalidOperationException("对象无效，请在有效性检测函数activeXObject中设置有效性");
            }
            return xobj;
        }
        public void Return(XObject<T> xObject, bool isDispose = false)
        {
            if (xObject == null)
            {
                throw new ArgumentNullException("xObject 不能为空！");
            }
            /*
             * 主动释放的释放
             * 超出最大闲置数量的释放
             * 无效的释放
             */
            if (isDispose || _activedTransportCount > xcfg.MaxIdle || !(ActiveXObject?.Invoke(xObject.Value) == true))
            {
                DisposeXObject(xObject);
                xObject.Pool = null;
                InterlockedDecrement();
                return;
            }
            xObject.LastDateTime = DateTime.Now;
            pools.Push(xObject);
            InterlockedDecrement();
            ResetEvent.Set();
        }


        private void Dispose(bool disposing)
        {
            if (!disposed)
            {
                if (disposing)
                {
                    try
                    {
                        while (pools.TryPop(out XObject<T> xobj))
                        {
                            //Pool 释放的时候XObject不再归还到Pool
                            DisposeXObject?.Invoke(xobj);
                            xobj.Pool = null;
                        }
                    }
                    catch (Exception)
                    {

                    }
                }
                disposed = true;
            }
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// 创建XObject对象
        /// </summary>
        public Func<T> CreateXObject { get; set; } = () => { return default(T); };
        /// <summary>
        /// 获取XObject对象之前验证True 有效
        /// </summary>
        public Func<T, bool> ActiveXObject { get; set; } = x => { return true; };
        /// <summary>
        /// 释放XObject时候触发
        /// </summary>
        public Action<XObject<T>> DisposeXObject { get; set; } = x => { };
        /// <summary>
        /// 移除长度为count的元素
        /// </summary>
        /// <param name="count">除元素的长度count</param>
        private void DisposeLongIdleXObject(int count)
        {
            int startIndex = pools.Count - count;
            XObject<T>[] popXObjects = new XObject<T>[count];
            pools.TryPopRange(popXObjects, 0, count);
            for (int i = 0; i < popXObjects.Length; i++)
            {
                Return(popXObjects[i], true);
            }
        }
        /// <summary>
        /// 每隔10秒检测一次清理30秒未使用的对象数量的对象
        /// (如果存在对象30未使用，说明对象池有对象长时间闲置未使用)则从头部弹出一定数量的对象释放掉
        /// </summary>
        private void StartTaskClearLongIdleXObject()
        {
#if net40
            Task.Factory.StartNew(() =>
#else
            Task.Factory.StartNew(async () =>
#endif
            {
                while (!disposed)
                {
#if net40
                    Thread.Sleep(10000);
#else
                    await Task.Delay(10000);
#endif
                    try
                    {
                        var removeCount = 0;
                        var now = DateTime.Now.AddSeconds(-30);
                        var _pools = pools.ToList();
                        for (int i = _pools.Count - 1; i >= xcfg.MinIdle; i--)
                        {
                            if (_pools[i].LastDateTime < now)
                            {
                                removeCount++;
                            }
                        }
                        if (removeCount > 0 && removeCount <= (pools.Count - xcfg.MinIdle))
                        {
                            DisposeLongIdleXObject(removeCount);
                        }
                    }
                    finally { }
                }
            }, TaskCreationOptions.LongRunning);
        }

    }

    public class XObject<T> : IDisposable
    {
        public XObjectPool<T> Pool { get; internal set; }

        public T Value { get; internal set; }
        public DateTime LastDateTime { get; set; } = DateTime.Now;

        public void Dispose()
        {
            if (Pool == null)
            {
                return;
            }
            Pool.Return(this, false);
        }
    }
    public class XPoolConfiguration
    {
        public int MaxActive { get; set; } = 500;
       
        public int MinIdle { get; set; } = Environment.ProcessorCount * 2;

        public int MaxIdle { get; set; } = 50;
    }
    #endregion
}
